package org.apache.ibatis.test.types;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;

import java.io.IOException;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * All rights Reserved, Designed By www.super-bytecode.com
 *
 * @ProjectName: source-mybatis3.5.8
 * @Package: org.apache.ibatis.test.types
 * @ClassName: JSONTypeHandler
 * @Description: [JSON字段类型处理器]
 * <p> </p>
 * @Author: [Lance Ting]
 * @Date: 2023/12/26 14:09
 * @Version: V1.0
 * @Copyright: 2023 www.super-bytecode.com Inc. All rights reserved.
 * TODO: 注意,本文件Lance Ting所作,如果转载或使用请标明具体出处!
 **/
@Slf4j
@MappedJdbcTypes(JdbcType.VARCHAR)
public class JacksonTypeHandler<T> extends BaseTypeHandler<T> {
  private static final ObjectMapper objectMapper;
  private final Class<T> type;

  static {
    objectMapper = new ObjectMapper();
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
  }

  @SuppressWarnings("unchecked")
  public JacksonTypeHandler() {
    this.type = (Class<T>) HashMap.class;
  }

  public JacksonTypeHandler(Class<T> type) {
    if (log.isTraceEnabled()) {
      log.trace("JacksonTypeHandler(" + type + ")");
    }
    if (null == type) {
      throw new PersistenceException("Type argument cannot be null");
    }
    this.type = type;
  }

  //
  @Override
  public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
    return parse(rs.getString(columnName));
  }

  @Override
  public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
    return parse(rs.getString(columnIndex));
  }

  @Override
  public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
    return parse(cs.getString(columnIndex));
  }

  @Override
  public void setNonNullParameter(PreparedStatement ps, int columnIndex, T parameter, JdbcType jdbcType) throws SQLException {
    ps.setString(columnIndex, toJsonString(parameter));
  }

  //
  private T parse(String json) {
    try {
      if (json == null || json.isEmpty()) {
        return null;
      }
      return objectMapper.readValue(json, type);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  private String toJsonString(T obj) {
    try {
      return objectMapper.writeValueAsString(obj);
    } catch (JsonProcessingException e) {
      throw new RuntimeException(e);
    }
  }

}
